1
|
|
|
/** |
2
|
|
|
* Parses for pattern's documentation |
3
|
|
|
* |
4
|
|
|
* |
5
|
|
|
* @package Patternlibrary |
6
|
|
|
*/ |
7
|
1 |
|
var async = require('async'); |
8
|
1 |
|
var extend = require('util')._extend; |
9
|
1 |
|
var format = require('string-template'); |
10
|
1 |
|
var fm = require('front-matter'); |
11
|
1 |
|
var fs = require('fs'); |
12
|
1 |
|
var glob = require('glob'); |
13
|
1 |
|
var globAll = require('glob-all'); |
14
|
1 |
|
var path = require('path'); |
15
|
1 |
|
var chalk = require('chalk'); |
16
|
1 |
|
var extend = require('extend'); |
17
|
1 |
|
var sanatizeType = require('../util/sanatize-patterntype'); |
18
|
|
|
|
19
|
1 |
|
module.exports = function(file, opts, cb) { |
20
|
|
|
var _this = this; // this => Supercollider ref, this.$PL => Patternlibrary ref |
21
|
|
|
var page = {}; |
22
|
|
|
var pageData = fm(file.contents.toString()); |
23
|
|
|
var relativePatternPath = path.relative( this.options.partials, path.dirname(file.path) ); |
24
|
|
|
|
25
|
2 |
|
if (typeof(opts) === 'function') { |
26
|
|
|
cb = opts; |
27
|
|
|
opts = {}; |
28
|
|
|
} |
29
|
|
|
// Global attributes |
30
|
|
|
page = pageData.attributes; |
31
|
|
|
//page.__fm = pageData.attributes; |
32
|
|
|
page.docs = ''; |
33
|
|
|
page.fileName = path.relative(process.cwd(), file.path); |
34
|
|
|
page._adapterData = {}; |
35
|
|
|
page._adapterFiles = {}; |
36
|
|
|
page.relatedFiles = []; |
37
|
|
|
|
38
|
|
|
// Catch Markdown errors |
39
|
2 |
|
if (this.markdown) { |
40
|
|
|
try { |
41
|
|
|
page.docs = this.markdown.render(pageData.body); |
42
|
|
|
} |
43
|
|
|
catch (e) { |
44
|
|
|
throw new Error('Markdown error: ' + e.message); |
45
|
|
|
} |
46
|
|
|
} |
47
|
|
|
else { |
48
|
|
|
page.docs = pageData.body; |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
// check for other (mandatory) page.{doctype} |
52
|
2 |
|
if (!page.source) { |
53
|
|
|
page.source = path.join(relativePatternPath, './index.html'); |
54
|
|
|
} else { |
55
|
|
|
page.source = path.join(relativePatternPath, page.source); |
56
|
|
|
} |
57
|
2 |
|
if (!page.sourcecode) { page.sourcecode = page.source; } |
58
|
2 |
|
if (!page.example) { page.example = page.source; } |
59
|
2 |
|
if (!page.specs) { page.specs = page.source; } |
60
|
2 |
|
if (!page.changelog) { page.changelog = path.join( relativePatternPath, './changelog.md'); } |
61
|
2 |
|
if (!page.tests) { page.tests = path.join( relativePatternPath, './test.js'); } |
62
|
2 |
|
if (!page.gitinfo) { page.gitinfo = path.join( relativePatternPath, ''); } |
63
|
|
|
|
64
|
|
|
// Run each adapter's parser, if the page references it |
65
|
|
|
var parseTasks = {}; |
66
|
|
|
for (var lib in this.adapters) { |
67
|
2 |
|
if (page[lib]) { |
68
|
|
|
|
69
|
|
|
// Placed inside an IIFE so the value of lib is correct for each function call |
70
|
|
|
(function(lib) { |
71
|
|
|
var collider = { |
72
|
|
|
"options" : _this.options, |
73
|
|
|
"filePath" : path.dirname(page.fileName), //path.relative( _this.$PL.Config.get('partials'), path.dirname(file.path) ), |
74
|
|
|
"pattern" : pageData.title |
75
|
|
|
}; |
76
|
|
|
|
77
|
|
|
parseTasks[lib] = function(cb) { |
78
|
|
|
// Store the original value of the YML property so it can be accessed later if needed |
79
|
|
|
page._adapterData[lib] = page[lib]; |
80
|
|
|
|
81
|
|
|
// find correct path for 'page[lib]'... |
82
|
|
|
var adapterFiles = findPath( page[lib], collider ); |
83
|
|
|
page._adapterFiles[lib] = adapterFiles; |
84
|
|
|
|
85
|
|
|
// Then find the configuration for the adapter... |
86
|
2 |
|
var config = extend(_this.adapters[lib].config, _this.options.adapters[lib] || { |
87
|
|
|
|
88
|
|
|
}); |
89
|
|
|
|
90
|
|
|
// ... and run it |
91
|
|
|
_this.adapters[lib](adapterFiles, config, cb, _this); |
92
|
|
|
}; |
93
|
|
|
|
94
|
|
|
parseTasks[lib + '-files'] = function(cb) { |
95
|
|
|
addFiles(page.relatedFiles, page._adapterFiles[lib], function(files) { |
96
|
|
|
page.relatedFiles = files; |
97
|
|
|
cb(); |
98
|
|
|
}); |
99
|
|
|
}; |
100
|
|
|
|
101
|
|
|
})(lib); |
102
|
|
|
|
103
|
|
|
} |
104
|
|
|
} // for(adapters) |
105
|
|
|
|
106
|
|
|
async.parallel(parseTasks, function(err, results) { |
107
|
|
|
for (var i in results) { |
108
|
|
|
page[i] = results[i]; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
var patternname = sanatizeType(path.dirname(page.source)); |
112
|
|
|
_this.data.patterns[patternname] = extend(_this.data.patterns[patternname], page); |
113
|
|
|
//_this.updateDataFile(); |
114
|
|
|
_this.log.info('parse tasks finished for "'+chalk.green(patternname)+'"'); |
115
|
|
|
|
116
|
|
|
// debug the main data tree: _this.$PL.debug('tree: ', _this.data); |
117
|
|
|
cb(null, page); |
118
|
|
|
}); |
119
|
|
|
} |
120
|
|
|
|
121
|
|
|
/** |
122
|
|
|
* find array item-key by object-key value in an array of objects |
123
|
|
|
* |
124
|
|
|
* @param array |
125
|
|
|
* @param key |
126
|
|
|
* @param value |
127
|
|
|
* @returns integer >= 0 on success, -1 on failure |
128
|
|
|
*/ |
129
|
|
|
function findByKey(array, key, value) { |
130
|
|
|
for (var i in array) { |
131
|
4 |
|
if (array[i][key] && array[i][key] === value) { |
132
|
|
|
return i; |
133
|
|
|
} |
134
|
|
|
} |
135
|
|
|
return -1; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* find 'correct' path for '(related)File' globs |
140
|
|
|
* |
141
|
|
|
* @param fileGlobs fileGlobs to look for |
142
|
|
|
* @param collider config |
143
|
|
|
* @returns fileGlob |
144
|
|
|
*/ |
145
|
|
|
function findPath(fileGlobs, collider) { |
146
|
|
|
|
147
|
|
|
// look in "partials"/"patterns"' filepath for "file" |
148
|
2 |
|
if ( fs.existsSync( path.join(collider.filePath, fileGlobs) ) ) { |
149
|
|
|
return ( path.join(collider.filePath, fileGlobs) ); |
150
|
|
|
} |
151
|
|
|
// look in "partials"/"patterns"' filepath for "*/**/file" |
152
|
2 |
|
if ( fs.existsSync( path.join(collider.filePath, "*/**/", fileGlobs) ) ) { |
153
|
|
|
return ( path.join(collider.filePath, "*/**/", fileGlobs) ); |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
// look in "partials"/"patterns" for "type/name/file" --- (fileGlobs != 'index.html') && |
157
|
2 |
|
if ( fs.existsSync( path.join(collider.options.partials, fileGlobs) ) ) { |
158
|
|
|
return ( path.join(collider.options.partials, fileGlobs) ); |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
// org. file path |
162
|
2 |
|
if (fs.existsSync( fileGlobs ) ) { |
163
|
|
|
return ( fileGlobs ); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
// if all fail, return given globs |
167
|
|
|
return(fileGlobs) |
168
|
|
|
} |
169
|
|
|
|
170
|
|
|
/** |
171
|
|
|
* add related files to task |
172
|
|
|
* |
173
|
|
|
* @param files related files |
174
|
|
|
* @param fileGlobs files to look for |
175
|
|
|
* @param cb async callback |
176
|
|
|
*/ |
177
|
|
|
function addFiles(files, fileGlobs, cb) { |
178
|
|
|
var searchGlob = fileGlobs; |
179
|
|
|
globAll(searchGlob, function(err, newFiles) { |
180
|
|
|
cb(files.concat(newFiles)); |
181
|
|
|
}); |
182
|
|
|
} |
183
|
|
|
|